import serial
import wx
import struct
import sys, glob
import time
import logging
import wx.aui 
import wx.lib.newevent

import accomm
import VirtualUnit as vu

import comlog


logging.basicConfig(level=logging.WARNING,
                    format='%(asctime)s %(levelname)s %(message)s')

columns = ["Time", "Data"]


class MainFrame(wx.Frame):
    def __init__(self):
        wx.Frame.__init__(self, None, -1,
                          "Cubic",
                          size=(700,500))
        self.list = None
        self.editable = False
        self.sserialList = {}
        
        # min size for the frame itself isn't completely done.
        # see the end up FrameManager::Update() for the test
        # code. For now, just hard code a frame minimum size
        self.SetMinSize(wx.Size(400, 300))
        # tell FrameManager to manage this frame        
        self._mgr = wx.aui.AuiManager()
        self._mgr.SetManagedWindow(self)        
        
        self.MakeMenu()
        #self.MakeListCtrl()

        
        
        self.CreateStatusBar()
        self.SetStatusText("Disconnected")
        
        self.MakeToolBar()    
        
        conn = self.addSerialConn("Conn #1", "COM1", 9600)
        
        
#        pvpnl = PacketViewPanel(self)
        pvpnl = comlog.ComlogPanel(self, conn)
        pvpnl.start()
        
#        pvpnl2 = PacketViewPanel(self)
        #unitmgrpnl = VirtualUnitManagerPanel(self, -1)
        sashpnl = TestSashPanel(self)
        
        msgpnl = MessagePanel(self)
        
        self._mgr.AddPane(pvpnl, wx.aui.AuiPaneInfo().
                          Name("packetview").Caption("Packet View").
                          Bottom().Layer(0).Position(1).CloseButton(True).MinimizeButton(True).
                          MaximizeButton(True).Row(0).BestSize((550,300)))
#        self._mgr.GetPane("test8").Show().Left().Layer(0).Row(0).Position(0)
        self._mgr.AddPane(msgpnl, wx.aui.AuiPaneInfo().
                          Name("message").Caption("Message").
                          Bottom().Layer(0).Position(0).CloseButton(False).Floatable(False).
                          MaximizeButton(True).Row(0).BestSize((550,300)))

        
#        self._mgr.AddPane(modetreepnl, wx.aui.AuiPaneInfo().
#                          Name("packetvie").Caption("Packet View").
#                          Left().Layer(0).Position(1).CloseButton(True).MinimizeButton(True).
#                          MaximizeButton(True).Row(0).BestSize((550,300)))


        self._mgr.AddPane(sashpnl, wx.aui.AuiPaneInfo().Name("html_content").
                          CenterPane())  

#        self._mgr.GetPane("html_content").Show()

        self._mgr.Update()
                          

        
        self.packetRecoder = PacketRecoder(conn)
        self.packetRecoder.setView(pvpnl)
        
#        self.vumgr = vu.UnitManager(conn)
#        self.vumgr.createIndoorUnit()
#        

        
        
    def addSerialConn(self, name, port="COM1", baudrate=9600):
        conn = accomm.SharableSerial(port, baudrate)
        self.sserialList[name] = conn
                
        return conn
    
    
    
    
    def MakeListCtrl(self, otherflags=0):
        # if we already have a listctrl then get rid of it
        if self.list:
            self.list.Destroy()

        if self.editable:
            otherflags |= wx.LC_EDIT_LABELS
            
        # load some images into an image list
        il = wx.ImageList(16,16, True)
        for name in glob.glob("smicon??.png"):
            bmp = wx.Bitmap(name, wx.BITMAP_TYPE_PNG)
            il_max = il.Add(bmp)

        # create the list control
        self.list = wx.ListCtrl(self, -1, style=wx.LC_REPORT|otherflags)

        # assign the image list to it
        self.list.AssignImageList(il, wx.IMAGE_LIST_SMALL)

        # Add some columns
        for col, text in enumerate(columns):
            self.list.InsertColumn(col, text)

#        # add the rows
#        for row, item in enumerate(data.rows):
#            index = self.list.InsertStringItem(sys.maxint, item[0])
#            for col, text in enumerate(item[1:]):
#                self.list.SetStringItem(index, col+1, text)
#
#            # give each item a random image
#            img = random.randint(0, il_max)
#            self.list.SetItemImage(index, img, img)
#
#            # set the data value for each item to be its position in
#            # the data list
#            self.list.SetItemData(index, row)
            
                
        # set the width of the columns in various ways
        self.list.SetColumnWidth(0, 120)
        self.list.SetColumnWidth(1, 400)

        # bind some interesting events
        self.Bind(wx.EVT_LIST_ITEM_SELECTED, self.OnItemSelected, self.list)
        self.Bind(wx.EVT_LIST_ITEM_DESELECTED, self.OnItemDeselected, self.list)
        self.Bind(wx.EVT_LIST_ITEM_ACTIVATED, self.OnItemActivated, self.list)

        # in case we are recreating the list tickle the frame a bit so
        # it will redo the layout
        self.SendSizeEvent()
        

    def MakeMenu(self):
        mbar = wx.MenuBar()
        menu = wx.Menu()
        item = menu.Append(-1, "E&xit\tAlt-X")
        self.Bind(wx.EVT_MENU, self.OnExit, item)
        mbar.Append(menu, "&File")

        menu = wx.Menu()
        item = menu.Append(-1, "Sort ascending")
        self.Bind(wx.EVT_MENU, self.OnSortAscending, item)        
        item = menu.Append(-1, "Sort descending")
        self.Bind(wx.EVT_MENU, self.OnSortDescending, item)
        item = menu.Append(-1, "Sort by submitter")
        self.Bind(wx.EVT_MENU, self.OnSortBySubmitter, item)

        menu.AppendSeparator()
        item = menu.Append(-1, "Show selected")
        self.Bind(wx.EVT_MENU, self.OnShowSelected, item)        
        item = menu.Append(-1, "Select all")
        self.Bind(wx.EVT_MENU, self.OnSelectAll, item)
        item = menu.Append(-1, "Select none")
        self.Bind(wx.EVT_MENU, self.OnSelectNone, item)

        menu.AppendSeparator()
        item = menu.Append(-1, "Set item text colour")
        self.Bind(wx.EVT_MENU, self.OnSetTextColour, item)
        item = menu.Append(-1, "Set item background colour")
        self.Bind(wx.EVT_MENU, self.OnSetBGColour, item)

        menu.AppendSeparator()
        item = menu.Append(-1, "Enable item editing", kind=wx.ITEM_CHECK)
        self.Bind(wx.EVT_MENU, self.OnEnableEditing, item)
        item = menu.Append(-1, "Edit current item")
        self.Bind(wx.EVT_MENU, self.OnEditItem, item)
        mbar.Append(menu, "&Demo")

        menu = wx.Menu()
        item = menu.Append(-1, "&Connect...")
        self.idConnectMenu = item.GetId()
        self.Bind(wx.EVT_MENU, self.OnConnect, item)        
        item = menu.Append(-1, "&Disconnect")
        item.Enable(False)
        self.idDisconnectMenu = item.GetId()
        self.Bind(wx.EVT_MENU, self.OnDisconnect, item)
        mbar.Append(menu, "&Connections")

        self.SetMenuBar(mbar)

    def MakeToolBar(self):
        tbar = self.CreateToolBar()
#        text = wx.StaticText(tbar, -1, "Filter")
#        cmb = wx.ComboBox(tbar, -1, choices=["AA", "BB", "CC"])
#        tbar.AddControl(text)
#        tbar.AddControl(cmb)
        tbar.Realize()
        



    def OnConnect(self, evt):
        mbar = self.GetMenuBar()
        mbar.Enable(self.idConnectMenu, False)
        mbar.Enable(self.idDisconnectMenu, True)
        self.SetStatusText("Connected[COM3:9600]")
        self.sserialList['Conn #1'].open()
        logging.debug("Port Connect")
    
    def OnDisconnect(self, evt):
        mbar = self.GetMenuBar()
        mbar.Enable(self.idConnectMenu, True)
        mbar.Enable(self.idDisconnectMenu, False)
        self.SetStatusText("Disconnected")
        self.sserialList['Conn #1'].close()

#        if self.thread is not None:
#            self.alive.clear()          #clear alive event for thread
#            
#            while self.thread.isAlive():
#                self.thread.join(3)          #wait until thread has finished
#                if self.thread.isAlive():    
#                    logging.critical("Fail thread stop")
#                
#            self.thread = None
        logging.debug("Port Disconnect")

  
    def OnExit(self, evt):
        self.Close()


    def OnItemSelected(self, evt):
        item = evt.GetItem()
        print "Item selected:", item.GetText()
        
    def OnItemDeselected(self, evt):
        item = evt.GetItem()
        print "Item deselected:", item.GetText()

    def OnItemActivated(self, evt): 
        item = evt.GetItem()
        print "Item activated:", item.GetText()

    def OnSortAscending(self, evt):
        # recreate the listctrl with a sort style
        self.MakeListCtrl(wx.LC_SORT_ASCENDING)
        
    def OnSortDescending(self, evt):
        # recreate the listctrl with a sort style
        self.MakeListCtrl(wx.LC_SORT_DESCENDING)

    def OnSortBySubmitter(self, evt):
        def compare_func(row1, row2):
            # compare the values in the 4th col of the data
#            val1 = data.rows[row1][3]
#            val2 = data.rows[row2][3]
#            if val1 < val2: return -1
#            if val1 > val2: return 1
            return 0

        self.list.SortItems(compare_func)
        


    def OnShowSelected(self, evt):
        print "These items are selected:"
        index = self.list.GetFirstSelected()
        if index == -1:
            print "\tNone"
            return
        while index != -1:
            item = self.list.GetItem(index)
            print "\t%s" % item.GetText()
            index = self.list.GetNextSelected(index)
            
    def OnSelectAll(self, evt):
        for index in range(self.list.GetItemCount()):
            self.list.Select(index, True)
    
    def OnSelectNone(self, evt):
        index = self.list.GetFirstSelected()
        while index != -1:
            self.list.Select(index, False)
            index = self.list.GetNextSelected(index)

    
    def OnSetTextColour(self, evt):
        dlg = wx.ColourDialog(self)
        if dlg.ShowModal() == wx.ID_OK:
            colour = dlg.GetColourData().GetColour()
            index = self.list.GetFirstSelected()
            while index != -1:
                self.list.SetItemTextColour(index, colour)
                index = self.list.GetNextSelected(index)
        dlg.Destroy()

    def OnSetBGColour(self, evt):
        dlg = wx.ColourDialog(self)
        if dlg.ShowModal() == wx.ID_OK:
            colour = dlg.GetColourData().GetColour()
            index = self.list.GetFirstSelected()
            while index != -1:
                self.list.SetItemBackgroundColour(index, colour)
                index = self.list.GetNextSelected(index)
        dlg.Destroy()


    def OnEnableEditing(self, evt):
        self.editable = evt.IsChecked()
        self.MakeListCtrl()
        
    def OnEditItem(self, evt):
        index = self.list.GetFirstSelected()
        if index != -1:
            self.list.EditLabel(index)
        
RECODER_BUFFER_SIZE = 5000


class PacketRecoder:
    def __init__(self, serial = None):
        self.link = accomm.Linker(accomm.LK_RX_METHOD_AC14 | accomm.LK_RX_METHOD_AC7)
        self.link.addListener(self.listener)
        if serial:
            self.link.connect(serial)
    
        self.pk_rep = []
        self.views = []
        self.newItemIdx = 0

    
    def clear(self):
        del self.pk_rep[:]
        
    def listener(self, data):
        self.pk_rep.append(data)
        
        if len(self.pk_rep) > RECODER_BUFFER_SIZE:
            del self.pk_rep[0]
            if self.newItemIdx > 0:
                self.newItemIdx = self.newItemIdx - 1
            
        self.updateViews()
        
    def updateViews(self):
        for v in self.views:
            evt = UpdateViewEvent(repo = self, data=self.pk_rep[self.newItemIdx:])
            wx.PostEvent(v, evt)
            #v.update(self)
        
            
        self.newItemIdx = len(self.pk_rep)
            
    def setView(self, view):
        self.views.append(view)
        
    def removeView(self, view):
        self.views.remove(view)    

        
(UpdateViewEvent, EVT_UPDATE_VIEW) = wx.lib.newevent.NewEvent()
        
class PacketViewPanel(wx.Panel):
    def __init__(self, parent):
        wx.Panel.__init__(self, parent)
        
        sizer = wx.BoxSizer(wx.VERTICAL)
#        conbox = wx.Choice(self, -1, choices=["C1", "C2"])
#        conbox.Select(0)
                             
#        sizer.Add(conbox)
                    
        # create the list control
        self.list = wx.ListCtrl(self, -1, style=wx.LC_REPORT)
        # Add some columns
        for col, text in enumerate(columns):
            self.list.InsertColumn(col, text)
        
        self.list.SetColumnWidth(0, 200)
        self.list.SetColumnWidth(1, 300)
        # min size for the frame itself isn't completely done.
        # see the end up FrameManager::Update() for the test
        # code. For now, just hard code a frame minimum size
        #sizer.SetSizeHints(self)
        sizer.Add(self.list, 1, wx.EXPAND)
        self.SetSizer(sizer)        
        self.SetAutoLayout(True)
        
        self.Bind(EVT_UPDATE_VIEW, self.OnUpdate)
        
    
    def OnUpdate(self, evt):
        
        self.update(evt.repo, evt.data)
        
    
    def setListener(self, listener):
        self.listener = listener
        
    def update(self, repo, data):
        items = data

        
        index = self.list.InsertStringItem(sys.maxint, time.strftime("%a, %d %b %Y %H:%M:%S +0000", time.gmtime()))
        
        str = ""
        
        for item in items:
            for data in item:
                str = str + "%02X" % data + " "
            self.list.SetStringItem(index, 1, str)
    #        self.list.ScrollWindow(0,10)       
            self.list.ScrollList(0, 50)     
            
                

    def OnPaint(self, event):
        pass


 
class VirtualUnitManagerPanel(wx.Panel):
    listHeader = (
      "Enabled",
      "Address",
      "Operation",
      "Set Temp",
      "Room Temp",
      "EvaOut Temp",
      "EvaIn Temp",
    )
    def __init__(self, *args, **keys):
        wx.Panel.__init__(self, *args, **keys)
            
        siz = wx.BoxSizer(wx.VERTICAL)
        self.unitList = wx.ListCtrl(self, -1, 
                                    style=wx.LC_REPORT|wx.LC_HRULES|wx.LC_VRULES, 
                                    name="aaa")
        list = self.unitList

        list.SetFont(wx.Font(9, wx.FONTFAMILY_SWISS, wx.FONTSTYLE_NORMAL, wx.FONTWEIGHT_NORMAL))
        for idx, str in enumerate(self.listHeader):
            list.InsertColumn(idx, str)

        for x in range(10):
            a = list.InsertStringItem(x,"On", wx.LIST_FORMAT_CENTRE)
            list.SetStringItem(a, 1, chr(ord("0") + x))
            list.SetStringItem(a, 2, "XXX")
        
        siz.Add(wx.StaticText(self, -1, "Units"), 0, wx.LEFT | wx.RIGHT, 5)
        siz.Add(list, 1, wx.EXPAND, 0)
        
        self.SetSizer(siz)
        self.SetAutoLayout(True)
        


class TestSashPanel(wx.Panel):
    def __init__(self, parent):
        wx.Panel.__init__(self, parent, -1)

        winids = []
        
        topwin = wx.SashLayoutWindow(
            self, -1, wx.DefaultPosition, (200, 30), 
            wx.NO_BORDER|wx.SW_3D
        )
        
        topwin.SetDefaultSize((1000, 30))
        topwin.SetOrientation(wx.LAYOUT_HORIZONTAL)
        topwin.SetAlignment(wx.LAYOUT_TOP)
        topwin.SetBackgroundColour(wx.Colour(255, 0, 0))
        topwin.SetSashVisible(wx.SASH_BOTTOM, True)
                
        self.topWindow = topwin
        winids.append(topwin.GetId())

        bottomwin = wx.SashLayoutWindow(
                self, -1, wx.DefaultPosition, (200, 30), 
                wx.NO_BORDER|wx.SW_3D
                )

        bottomwin.SetDefaultSize((1000, 30))
        bottomwin.SetOrientation(wx.LAYOUT_HORIZONTAL)
        bottomwin.SetAlignment(wx.LAYOUT_BOTTOM)
        bottomwin.SetBackgroundColour(wx.Colour(0, 0, 255))
        bottomwin.SetSashVisible(wx.SASH_TOP, True)

        self.bottomWindow = bottomwin
        winids.append(bottomwin.GetId())

        self.remainingSpace = wx.Panel(self, -1, style=wx.SUNKEN_BORDER)

        self.Bind(
            wx.EVT_SASH_DRAGGED_RANGE, self.OnSashDrag,
            id=min(winids), id2=max(winids)
            )

        self.Bind(wx.EVT_SIZE, self.OnSize)


    def OnSashDrag(self, event):
        if event.GetDragStatus() == wx.SASH_STATUS_OUT_OF_RANGE:
#            self.log.write('drag is out of range')
            return

        eobj = event.GetEventObject()

        if eobj is self.topWindow:
#            self.log.write('topwin received drag event')
            self.topWindow.SetDefaultSize((1000, event.GetDragRect().height))

#        elif eobj is self.leftWindow1:
##            self.log.write('leftwin1 received drag event')
#            self.leftWindow1.SetDefaultSize((event.GetDragRect().width, 1000))
#
#
#        elif eobj is self.leftWindow2:
##            self.log.write('leftwin2 received drag event')
#            self.leftWindow2.SetDefaultSize((event.GetDragRect().width, 1000))

        elif eobj is self.bottomWindow:
#            self.log.write('bottomwin received drag event')
            self.bottomWindow.SetDefaultSize((1000, event.GetDragRect().height))

        wx.LayoutAlgorithm().LayoutWindow(self, self.remainingSpace)
        self.remainingSpace.Refresh()

    def OnSize(self, event):
        wx.LayoutAlgorithm().LayoutWindow(self, self.remainingSpace)

class MessagePanel(wx.Panel):
    def __init__(self, parent):
        wx.Panel.__init__(self, parent)
        
        sizer = wx.BoxSizer(wx.VERTICAL)
        msgbox = wx.TextCtrl(self, -1, style=wx.TE_MULTILINE |wx.TE_READONLY)   
        sizer.Add(msgbox, 1, wx.EXPAND)
        
        self.SetSizer(sizer)
        self.SetAutoLayout(True)
          
            
class DemoApp(wx.App):
    def OnInit(self):
        frame = MainFrame()
        self.SetTopWindow(frame)
        frame.Show()
        return True

        


    

if __name__ == "__main__":
    app = DemoApp(redirect=True)
    app.MainLoop()    
    
    #app = wx.PySimpleApp(0)
    #app.MainLoop()
